﻿using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebPartPages;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.UI.WebControls.WebParts;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;

namespace Sharepoint.PageManifestExtractor
{
    public static class Common
    {

        internal static Elements GetElementFromElementsXml()
        {
            XmlSerializer serializer = null;
            string elementFilePath = SPUtility.GetVersionedGenericSetupPath(Constants.ElementsTemplateFile, 15);

            using (FileStream xmlStream = new FileStream(elementFilePath, FileMode.Open))
            {
                using (XmlReader xmlReader = XmlReader.Create(xmlStream))
                {
                    serializer = new XmlSerializer(typeof(Elements));
                    return (Elements)serializer.Deserialize(xmlReader);
                }
            }
        }

        private static ElementsModuleFileProperty[] GetPropertyElements(SPFile oSPFile, out string FileContents)
        {
            ArrayList fileProperties = new ArrayList();
            byte[] fileBytes = null;
            System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
            string fileContents = string.Empty;
            string filePropertiesSubstring = string.Empty;
            XmlNamespaceManager namespaceManager = new XmlNamespaceManager(new NameTable());
            XmlParserContext xmlParserContext = null;
            XDocument xmlDocument = null;
            XNamespace xmlNamespace = null;
            ElementsModuleFileProperty fileMetadata = null;
            string fileTitleString = string.Empty;
            ElementsModuleFileProperty fileTitle = null;

            fileBytes = oSPFile.OpenBinary();

            fileContents = enc.GetString(fileBytes);
            FileContents = fileContents;

            if (fileContents.Contains(Constants.DocumentPropertiesTag))
            {
                filePropertiesSubstring =
                    fileContents.Substring(fileContents.IndexOf(Constants.DocumentPropertiesTag),
                    fileContents.IndexOf(Constants.DocumentPropertiesTagEnd) - fileContents.IndexOf(Constants.DocumentPropertiesTag) +
                    (Constants.DocumentPropertiesTagEnd).Length);

                namespaceManager.AddNamespace(Constants.MsoTag.Key, Constants.MsoTag.Value);
                namespaceManager.AddNamespace(Constants.MsdtTag.Key, Constants.MsdtTag.Value);

                xmlParserContext = new XmlParserContext(null, namespaceManager, null, XmlSpace.Default);

                using (XmlReader reader = XmlReader.Create(new StringReader(filePropertiesSubstring), null, xmlParserContext))
                {
                    xmlDocument = XDocument.Load(reader);
                    xmlNamespace = Constants.MsoTag.Value;

                    // Test Comment
                    foreach (XElement element in xmlDocument.Descendants(xmlNamespace + Constants.CustomDocumentPropertiesTag).Elements())
                    {
                        fileMetadata = new ElementsModuleFileProperty();
                        fileMetadata.Name = element.Name.LocalName;
                        fileMetadata.Value = SecurityElement.Escape(element.Value);
                        fileProperties.Add(fileMetadata);
                    }

                    if (fileContents.Contains(Constants.TitleTag))
                    {
                        fileTitleString =
                            fileContents.Substring(fileContents.IndexOf(Constants.TitleTag) + (Constants.TitleTag).Count(),
                            fileContents.IndexOf(Constants.TitleTagEnd) - fileContents.IndexOf(Constants.TitleTag) - (Constants.TitleTag).Count());
                        fileTitle = new ElementsModuleFileProperty();
                        fileTitle.Name = Constants.TitleText;
                        fileTitle.Value = SecurityElement.Escape(fileTitleString);
                        fileProperties.Add(fileTitle);
                    }

                }
            }

            if (fileProperties.Count > 0)
                return (ElementsModuleFileProperty[])fileProperties.ToArray(typeof(ElementsModuleFileProperty));
            else
                return null;
            
        }

        public static string UnescapeXML(this string s)
        {
            if (string.IsNullOrEmpty(s)) return s;

            string returnString = s;
            returnString = returnString.Replace("&apos;", "'");
            returnString = returnString.Replace("&quot;", "\"");
            returnString = returnString.Replace("&gt;", ">");
            returnString = returnString.Replace("&lt;", "<");
            returnString = returnString.Replace("&amp;", "&");

            return returnString;
        }

        [DllImport("advapi32.dll")]
        private static extern uint EventActivityIdControl(uint controlCode, ref Guid activityId);
        public static string GetToken(this SPContext currentContext)
        {
            try
            {
                if (currentContext != null)
                {
                    var guid = new Guid();
                    uint result = EventActivityIdControl(1, ref guid);
                    if (result == 0)
                        return guid.ToString().ToLowerInvariant();
                }
                return Guid.Empty.ToString();
            }
            catch (Exception)
            {
                return Guid.Empty.ToString();
            }
        }

        private static ElementsModuleFileAllUsersWebPart[] GetWebpartElements(SPFile oSPFile)
        {

            ArrayList webpartList = new ArrayList();
            SPLimitedWebPartManager webpartManager = null;
            ElementsModuleFileAllUsersWebPart webpartManifest = null;
            var webpartMarkup = new StringBuilder();

            webpartManager = oSPFile.Web.GetLimitedWebPartManager(oSPFile.Url, PersonalizationScope.Shared);
            

            foreach (System.Web.UI.WebControls.WebParts.WebPart webpart in webpartManager.WebParts)
            {
                webpartManifest = new ElementsModuleFileAllUsersWebPart();

                try
                {                    
                    webpartManifest.WebPartOrder = webpart.ZoneIndex;
                    webpartManifest.WebPartZoneID = webpartManager.GetZoneID(webpart);

                    using (var xmlTextWriter = new XmlTextWriter(new StringWriter(webpartMarkup)) { Formatting = Formatting.Indented })
                    {
                        webpartManager.ExportWebPart(webpart, xmlTextWriter);
                        webpartManifest.Value = Constants.CDataTag + EscapeCData(webpartMarkup.ToString()) + Constants.CDataTagEnd;
                        
                    }
                }
                catch (Exception ex)
                {
                    LoggingService.LogError(Constants.LogCategory, ex.Message);
                    webpartManifest.Value = string.Format(GetLocalizedString("WebpartErrorMessageFormat"), Constants.CDataTag, ex.Message.ToString(), GetCorrelationId(), Constants.CDataTagEnd);
                }

                webpartList.Add(webpartManifest);               
            }

            if (webpartList.Count > 0)
                return (ElementsModuleFileAllUsersWebPart[])webpartList.ToArray(typeof(ElementsModuleFileAllUsersWebPart));
            else
                return null;

        }

        private static string GetCorrelationId()
        {
            return SPContext.Current.GetToken();
        }

        private static string EscapeCData(string inputString)
        {
            if (inputString.ToUpperInvariant().Contains(Constants.CDataTag.ToUpperInvariant()))
            {
                return inputString.Replace("]]>", "]]]]><![CDATA[>");
            }
            else
                return inputString;
        }

        internal static ElementsModuleFile GetFileElement(this SPFile oSPFile)
        {
            string fileContents = string.Empty;
            ElementsModuleFile fileElement = new ElementsModuleFile();
            fileElement.IgnoreIfAlreadyExists = Constants.IgnoreIfAlreadyExists;
            fileElement.Type = Constants.GhostingType;
            fileElement.Path = GetLocalizedString("PathInFeatureFolderText") + "\\" + oSPFile.Name;
            fileElement.Url = oSPFile.Url.Replace(oSPFile.DocumentLibrary.RootFolder.Url, string.Empty).Trim('/');

            try
            {
                fileElement.Property = GetPropertyElements(oSPFile, out fileContents);
                fileElement.Contents = fileContents;
                fileElement.AllUsersWebPart = GetWebpartElements(oSPFile);

                return fileElement;
            }
            catch (Exception ex)
            {
                LoggingService.LogError(Constants.LogCategory, ex.Message);
                return null;
            }
        }

        internal static string GetLocalizedString(string KeyName)
        {
            return SPUtility.GetLocalizedString("$Resources:" + KeyName, Constants.ApplicationResourceFileName, (uint)Thread.CurrentThread.CurrentCulture.LCID);
        }

        public static CultureInfo GetCurrentCulture()
        {
            CultureInfo defaultCulture = CultureInfo.CreateSpecificCulture(Constants.DefaultCulture);
            string[] languages = HttpContext.Current.Request.UserLanguages;


            if (languages == null || languages.Length == 0)
                return defaultCulture;

            try
            {
                string language = languages[0].ToLowerInvariant().Trim();
                return CultureInfo.CreateSpecificCulture(language);
            }
            catch (ArgumentException)
            {
                return defaultCulture;
            }
        }

        internal static string ConvertToString(this Elements element)
        {
            XmlSerializer xmlSerializer = new XmlSerializer(typeof(Elements));
            StringBuilder elementText = new StringBuilder();
            XmlWriterSettings settings = new XmlWriterSettings();

            settings.OmitXmlDeclaration = true;
            settings.Indent = true;
            //settings.
            using (XmlWriter writer = XmlWriter.Create(elementText, settings))
            {
                xmlSerializer.Serialize(writer, element);
            }

            return elementText.ToString();
            
        }

        internal static Collection<SPFile> GetFiles(string pageUrl)
        {
            SPFile spFile = null;
            Collection<SPFile> files = new Collection<SPFile>();

            SPWeb web = SPContext.Current.Web;

            object file = web.GetFileOrFolderObject(pageUrl);
            if (file is SPFile)
            {
                spFile = (SPFile)file;

                if (spFile.Exists)
                    files.Add(spFile);
            }
            //SPFile spFile = web.GetFile(pageUrl);

            

            return files;

        }

        internal static Collection<SPFile> GetAllFiles(string pageListId)
        {
            Collection<SPFile> files = new Collection<SPFile>();
            Guid listId = new Guid(pageListId);
            SPWeb web = SPContext.Current.Web;
            SPDocumentLibrary documentLibrary = (SPDocumentLibrary)web.Lists.GetList(listId, false);

            //SPFolder documentLibrary = web.GetFolder(documentLibrary.Title);

            //SPFolder mylibrary = myweb.Folders["Name of library"];
            //SPFileCollection libraryFiles = documentLibrary.Files;

            foreach (SPListItem item in documentLibrary.Items)
            {
                files.Add(item.File);
            }

            return files;
        }
    }
}
